package lianxi11.v1;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import lianxi11.ExchangeService;
import lianxi11.ExchangeService.Money;

public class BestPriceFinder {

	
	private final List<Shop> shops =Arrays.asList(new Shop("BestPrice"),
												  new Shop("LetsSaveBig"),
												  new Shop("MyFavoriteShop"),
												  new Shop("BuyItAll")
												  );
	
	private final Executor executor =Executors.newFixedThreadPool(shops.size(),new ThreadFactory() {
		
		@Override
		public Thread newThread(Runnable r) {
			Thread t =new Thread(r);
			t.setDaemon(true);  //设置为守护线程
			return t;
		}
	});
	
	public List<String> findPricessSequential(String product){
		List<String> collect = shops.stream().map(shop -> shop.getName() +"price is"+ shop.getPrice(product))
						.collect(Collectors.toList());
		return collect;
	}
	
	public List<String> findPricesParallel(String product){
		List<String> collect = shops.parallelStream().map(shop -> shop.getName() +"price is"+ shop.getPrice(product))
						.collect(Collectors.toList());
	
		return collect;
	}
	
	public List<String> findPricesFuture(String product){
		List<CompletableFuture<String>> collect = shops.stream().map(shop ->CompletableFuture.supplyAsync(()->
				shop.getName() +" price is "+ shop.getPrice(product),executor
					)).collect(Collectors.toList());
		
		List<String> collect2 = collect.stream().map(
									CompletableFuture::join)   // join 跟get 类似获取值
									.collect(Collectors.toList());
		
		return collect2;
	}
	
	/**
	 * 
	 * @param product
	 * @return
	 */
	public List<String> findPricesInUSD(String product){
		List<CompletableFuture<Double>> priceFutures = new ArrayList<>();
		for(Shop shop : shops){
			CompletableFuture<Double> futurePriceInUSD =
						CompletableFuture.supplyAsync(() ->  shop.getPrice(product))
										 .thenCombine(
												 CompletableFuture.supplyAsync(()-> ExchangeService.getRate(Money.EUR, Money.USD)),
												 (price ,rate) -> price * rate 
										);
			priceFutures.add(futurePriceInUSD);					 
		}
		
		List<String> collect = priceFutures.stream().map(CompletableFuture::join)
							 .map(price -> "price is "+ price )
							 .collect(Collectors.toList());
		
		return  collect;
		
	}
	
	public List<String> findPricesInUSDJava7(String product){
		ExecutorService exexutor =Executors.newCachedThreadPool();
		
		List<Future<Double>> priceFutures = new ArrayList<>();
		for(Shop shop : shops){
			final Future<Double> futureRate= exexutor.submit(new Callable<Double>() {
				public Double call(){
					return ExchangeService.getRate(Money.EUR, Money.USD);
				}
			});
			
			Future<Double> futurePriceInUSD =exexutor.submit(new Callable<Double>() {
				public Double call(){
					try {
						double priceInEUR =shop.getPrice(product);
						return  priceInEUR * futureRate.get();
						
					} catch (Exception e) {
						throw new RuntimeException(e.getMessage(),e);
					}
				}
				
			});
			priceFutures.add(futurePriceInUSD);
		}
		
		List<String> prices = new ArrayList<>();
		for(Future<Double> priceFuture: priceFutures){
			try {
				prices.add("price is" + priceFuture.get());
			} catch (ExecutionException | InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		return prices;
	}
	
	public List<String> findPriceInUSD2(String product){
		List<CompletableFuture<String>> priceFutures = new ArrayList<>();
		
		for(Shop shop: shops){
			CompletableFuture<String> futurePricesInUID = CompletableFuture
					.supplyAsync(()-> shop.getPrice(product))
					.thenCombine(CompletableFuture
							.supplyAsync(()-> ExchangeService.getRate(Money.EUR, Money.USD))
							
							
							,(price ,rate) -> price * rate  )
					.thenApply(price -> shop.getName() + " price is " + price );
			
			priceFutures.add(futurePricesInUID);
					
		}
		
		List<String> collect = priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
		return collect ;
	}
	
	public List<String> findPricesInUSD3(String product){

		Stream<CompletableFuture<String>> stream = shops.stream().map(shop -> CompletableFuture.supplyAsync(()->shop.getPrice(product))
					  .thenCombine(
							  CompletableFuture
							  .supplyAsync(() -> ExchangeService.getRate(Money.EUR,  Money.USD))
							  
							  , (price , rate) -> price * rate)
					  .thenApply(price -> shop.getName() +" price is "+ price )); 
		
		List<CompletableFuture<String>> collect = stream.collect(Collectors.toList());
		List<String> collect2 = collect.stream().map(CompletableFuture::join).collect(Collectors.toList());
		
		return collect2;
	}
}
